package log

import (
	"bufio"
	"errors"
	"io"
	"os"
)

type LogWriter interface {
	io.Writer
	//获取可写日志类型
	GetLevels() Levels
	//判断是否写入
	IsWrite(lv Level) bool
	//写日志
	WriteLog(lv Level, content []byte)
	//关闭
	io.Closer
}

type WriterErrHandler func(w LogWriter, e error, log string)

type logWriterOptions struct {
	lvs       Levels           //输出的日志类型
	w         io.Writer        //输出的接口
	errHandle WriterErrHandler //输出失败消息回调
}

type LogWriterOption func(*logWriterOptions)

func LWLevel(lv Levels) LogWriterOption {
	return func(w *logWriterOptions) {
		w.lvs = lv
	}
}

func LWStrLevel(lv string) LogWriterOption {
	return func(w *logWriterOptions) {
		w.lvs = NewLevel(lv)
	}
}

func LWithWriter(writer io.Writer) LogWriterOption {
	return func(w *logWriterOptions) {
		w.w = writer
	}
}

func LWErrHandler(eh WriterErrHandler) LogWriterOption {
	return func(w *logWriterOptions) {
		w.errHandle = eh
	}
}

var defaultLogWriterOptions = logWriterOptions{
	lvs:       Lv_Debug,
	errHandle: func(LogWriter, error, string) {},
}

// 控制台输出
func NewConsoleWriter(opts ...LogWriterOption) LogWriter {
	opts = append(opts, LWithWriter(os.Stdout))
	return NewLogWriter(opts...)
}

// 输出日志
type logWriter struct {
	opts logWriterOptions
}

func NewLogWriter(opts ...LogWriterOption) LogWriter {
	w := &logWriter{
		opts: defaultLogWriterOptions,
	}
	for _, opt := range opts {
		opt(&w.opts)
	}
	return w
}

func (w *logWriter) GetLevels() Levels {
	return w.opts.lvs
}

func (w *logWriter) IsWrite(lv Level) bool {
	return w.opts.lvs.Enable(lv)
}

func (w *logWriter) Write(content []byte) (int, error) {
	return w.opts.w.Write(content)
}

func (w *logWriter) WriteLog(lv Level, content []byte) {
	if w.IsWrite(lv) {
		_, err := w.Write(content)
		if err != nil {
			w.opts.errHandle(w, err, string(content))
		}
	}
}

func (w *logWriter) Close() error {
	if clser, ok := w.opts.w.(io.Closer); ok {
		return clser.Close()
	}
	return nil
}

// 多个输出终端
type MultiWriter struct {
	writers []LogWriter
	//需要保存的日志, 根据Writers的Level进行推断
	lvs Levels
}

func NewMultiWriter(ws ...LogWriter) *MultiWriter {
	l := NOLOG
	for _, wr := range ws {
		l |= wr.GetLevels()
	}
	return &MultiWriter{ws, l}
}

func (w *MultiWriter) GetLevels() Levels {
	return w.lvs
}

func (w *MultiWriter) IsWrite(lv Level) bool {
	return w.lvs.Enable(lv)
}

func (w *MultiWriter) Write(content []byte) (int, error) {
	var r int
	var e error
	for _, wr := range w.writers {
		r, e = wr.Write(content)
		if e != nil {
			break
		}
	}
	return r, e
}

func (w *MultiWriter) WriteLog(lv Level, content []byte) {
	for _, wr := range w.writers {
		wr.WriteLog(lv, content)
	}
}

func (w *MultiWriter) Close() error {
	var err error
	for _, wr := range w.writers {
		e := wr.Close()
		if e != nil {
			err = e
		}
	}
	return err
}

// 异步输出终端
type PipeWriter struct {
	opts logWriterOptions
	pw   *io.PipeWriter
	pr   *io.PipeReader
}

func NewPipeWriter(opts ...LogWriterOption) *PipeWriter {
	p := &PipeWriter{
		opts: defaultLogWriterOptions,
	}
	for _, opt := range opts {
		opt(&p.opts)
	}
	p.initPipe()
	return p
}

func (w *PipeWriter) GetLevels() Levels {
	return w.opts.lvs
}

func (w *PipeWriter) IsWrite(lv Level) bool {
	return w.opts.lvs.Enable(lv)
}

func (w *PipeWriter) Write(content []byte) (int, error) {
	return w.pw.Write(content)
}

func (w *PipeWriter) WriteLog(lv Level, content []byte) {
	if w.IsWrite(lv) {
		_, err := w.pw.Write(content)
		if err != nil {
			if !errors.Is(err, io.ErrClosedPipe) {
				w.opts.errHandle(w, err, string(content))
			}
		}
	}
}

func (w *PipeWriter) Close() error {
	return w.pw.Close()
}

func (w *PipeWriter) initPipe() {
	if w.pw != nil {
		w.pw.Close()
	}
	if w.pr != nil {
		w.scanmsg()
	}
	pr, pw := io.Pipe()
	w.pr = pr
	w.pw = pw
	go w.readmsg()
}

func (w *PipeWriter) closeReader() {
	if w.pr != nil {
		w.pr.Close()
		w.pr = nil
		if clser, ok := w.opts.w.(io.Closer); ok {
			clser.Close()
		}
	}
}

func (w *PipeWriter) readmsg() {
	defer w.closeReader()
	buf := make([]byte, 4096)
	for {
		s, err := w.pr.Read(buf[:4096])
		if err != nil {
			if errors.Is(err, io.EOF) {
				break
			}
			w.opts.errHandle(w, err, "")
			break
		}
		_, err = w.opts.w.Write(buf[:s])
		if err != nil {
			w.opts.errHandle(w, err, string(buf[:s]))
		}
	}
}

func (w *PipeWriter) scanmsg() {
	defer w.closeReader()
	scanner := bufio.NewScanner(w.pr)
	scanner.Split(bufio.ScanRunes)
	for scanner.Scan() {
		buf := scanner.Bytes()
		_, err := w.opts.w.Write(buf)
		if err != nil {
			w.opts.errHandle(w, err, string(buf))
		}
	}
	if err := scanner.Err(); err != nil && errors.Is(err, io.EOF) {
		w.opts.errHandle(w, err, "")
	}
}
